#include "core/kernel.h"

namespace core {
	Kernel::Kernel() {
		mWriteMainList = 0;
		mReadMainList = 1;
		mWriteSyncList = 0;
		mReadSyncList = 1;
	}

	Kernel::~Kernel() {
		for (auto& ft: mBackgroundTasks)
			ft.wait();

		for (auto& ft: mSyncTasks[0])
			ft.wait();

		for (auto& ft: mSyncTasks[1])
			ft.wait();
	}

	void Kernel::add(TaskPtr task) {
		unsigned int flags = task->getTaskFlags();

		if (flags & Task::THREADSAFE) {
			if (flags & Task::FRAME_SYNC)
				mSyncTasks[mWriteSyncList].push_back(std::async([this, task] { execute(task); }));
			else
				mBackgroundTasks.push_back(std::async([this, task] { execute(task); }));
		}
		else
			mTaskList[mWriteMainList].push(task);
	}

	void Kernel::start() {
		mRunning = true;

		mChan.add<Task::TaskCompleted>(this);
		mChan.add<StopEvent>(this);

		while (mRunning) {
			TaskPtr t;
			
			if (mTaskList[mReadMainList].tryPop(t)) {
				execute(t);
			}
			else {
				synchronize();
				std::swap(mReadMainList, mWriteMainList);
			}

			std::this_thread::yield();
		}
	}

	void Kernel::synchronize() {
		ScopedLock lock(mSyncMutex);

		if (!mRunning)
			return;

		for (auto& ft: mSyncTasks[mReadSyncList])
			ft.wait();

		std::swap(mReadSyncList, mWriteSyncList);
	}

	void Kernel::execute(TaskPtr t) {
		mChan.broadcast(Task::TaskBeginning(t));
		t->run();
		mChan.broadcast(Task::TaskCompleted(t));
	}

	void Kernel::stop() {
		mRunning = false;
	}

	void Kernel::operator()(const Kernel::StopEvent& ) {
		stop();
	}

	void Kernel::operator()(const Task::TaskCompleted& tc) {
		if (mRunning && (tc.mTask->getTaskFlags() & Task::REPEATING))
			add(tc.mTask);
	}
}